#include "seatresmanager.hpp"

SeatResManager::SeatResManager()
{
    /*******************************************数据库初始化***********************************************/
    db = QSqlDatabase::addDatabase("QMYSQL");
    db.setHostName("localhost");
    db.setUserName("root");
    db.setPassword("1234");
    if(!db.open()) {
        qDebug() << "error!";
    } else {
        qDebug() << "Open";
    }
    std::vector<SqlTableData> seatInfoList = ReadDataFromTable("seatinfo");
    std::vector<SqlTableData> stuDataList = ReadDataFromTable("usertable");
    LoadSeatList(seatInfoList);
    LoadUserList(stuDataList);
    /*****************************************************************************************************/

    /*******************************************登陆界面初始化***********************************************/
    dialogLogin_ = new DialogLogin();
    dialogLogin_->show();
    connect(dialogLogin_, SIGNAL(submitUserLoginInfo(StuData)), this, SLOT(onPostLoginData(StuData)));
    connect(dialogLogin_, SIGNAL(submitUserRegisterInfo(StuData)), this, SLOT(onPostRegisterData(StuData)));
    /*****************************************************************************************************/

    /*******************************************TCP Server初始化***********************************************/
    server_ = new QTcpServer();
    server_->listen(QHostAddress::Any, 11000);
    connect(server_, SIGNAL(newConnection()), this, SLOT(onPostNewTcpClient()));
    /*****************************************************************************************************/

    /*******************************************定时器初始化***********************************************/
    timer = new QTimer(this);
    timer->setInterval(1000);
    timer->start();
    connect(timer, SIGNAL(timeout()), this, SLOT(onTimerTimeOut()));
    /*****************************************************************************************************/
}

/**
 * @brief SeatResManager::onTimerTimeOut 定时器超时函数，每秒执行一次，判断用户是否在预约时间之前签到
 */
void SeatResManager::onTimerTimeOut()
{
    uint timeNow = QDateTime::currentDateTime().toTime_t();
    qDebug() << "now time:" << timeNow;
    std::map<QString, StuData>::iterator iter = stuData_.begin();
    for (; iter != stuData_.end(); iter++)
    {
        StuData *stuData = &iter->second;
        bool updateFlag = false;
        if (stuData->limit == 0)
        {
            if (!stuData->resTime.isEmpty() && stuData->resTime.toUInt() < timeNow && stuData->resState == RESSTATE_NOT_CHECKED_IN)
            {
                stuData->limitCnt++;
                updateFlag = true;
            }
            if (stuData->limitCnt >= 3)
            {
                stuData->limit = 1;
                stuData->limitCnt = 0;
                //                stuData.limitTime = QString::number(timeNow + 60 * 60 * 24 * 7);
                stuData->limitTime = QString::number(timeNow + 60);
                updateFlag = true;
            }
        }
        else
        {
            if (!stuData->limitTime.isEmpty() && stuData->limitTime.toUInt() < timeNow)
            {
                stuData->limit = 0;
                stuData->limitCnt = 0;
                stuData->limitTime.clear();
                EditTableItem("usertable", {"stu_id", stuData->stuid}, {"limit", QString::number(stuData->limit)});
                EditTableItem("usertable", {"stu_id", stuData->stuid}, {"limit_time", stuData->limitTime});
                EditTableItem("usertable", {"stu_id", stuData->stuid}, {"limit_cnt", QString::number(stuData->limitCnt)});
                std::vector<SqlTableData> stuDataList = ReadDataFromTable("usertable"); //更新用户列表至缓存
                LoadUserList(stuDataList);
                emit PostUserState(iter->second);
                break;
            }
        }
        if (updateFlag)
        {
            UnBindSeat(stuData->stuid);
            break;
        }
    }
}

/**
 * @brief SeatResManager::UnBindSeat 取消指定用户的座位预约
 * @param stuid
 */
void SeatResManager::UnBindSeat(QString stuid)
{
    std::map<QString, StuData>::iterator iter = stuData_.find(stuid);
    if (iter == stuData_.end())
        return;
    StuData *stuData = &iter->second;
    std::map<QString, SeatInfo>::iterator iterSeatInfo = seatInfo_.find(stuData->resSeatAddr);
    if (iterSeatInfo == seatInfo_.end())
        return;
    SeatInfo seatInfo = iterSeatInfo->second;

    stuData->resTime.clear();
    stuData->resState = RESSTATE_NULL;
    stuData->resSeatAddr.clear();

    EditTableItem("usertable", {"stu_id", stuData->stuid}, {"limit", QString::number(stuData->limit)});
    EditTableItem("usertable", {"stu_id", stuData->stuid}, {"limit_time", stuData->limitTime});
    EditTableItem("usertable", {"stu_id", stuData->stuid}, {"limit_cnt", QString::number(stuData->limitCnt)});
    EditTableItem("usertable", {"stu_id", stuData->stuid}, {"res_seat_addr", stuData->resSeatAddr});
    EditTableItem("usertable", {"stu_id", stuData->stuid}, {"res_state", QString::number(stuData->resState)});
    EditTableItem("usertable", {"stu_id", stuData->stuid}, {"res_time", stuData->resTime});
    EditTableItem("seatinfo", {"seat_id", seatInfo.seatAddr}, {"stu_id", ""});
    EditTableItem("seatinfo", {"seat_id", seatInfo.seatAddr}, {"start_time", ""});
    EditTableItem("seatinfo", {"seat_id", seatInfo.seatAddr}, {"seat_state", "0"});

    std::vector<SqlTableData> stuDataList = ReadDataFromTable("usertable"); //更新用户列表至缓存
    std::vector<SqlTableData> seatInfoList = ReadDataFromTable("seatinfo");
    LoadUserList(stuDataList);
    LoadSeatList(seatInfoList);
    iter = stuData_.find(seatInfo.stuid);
    if (iter == stuData_.end())
        return;
    emit PostUserState(iter->second);
    emit PostSeatTable(seatInfo_);
}

/**
 * @brief SeatResManager::onPostNewTcpClient TCP客户端连接时的槽函数
 */
void SeatResManager::onPostNewTcpClient()
{
    QTcpSocket *socket;
    //如果有新的连接就取出
    while(server_->hasPendingConnections())
    {
        //nextPendingConnection返回下一个挂起的连接作为已连接的QTcpSocket对象
        //套接字是作为服务器的子级创建的，这意味着销毁QTcpServer对象时会自动删除该套接字。
        //最好在完成处理后显式删除该对象，以避免浪费内存。
        //返回的QTcpSocket对象不能从另一个线程使用，如有需要可重写incomingConnection().
        socket=server_->nextPendingConnection();
        clientMap_[socket->peerAddress().toString()] = socket;

        //关联相关操作的信号槽
        //收到数据，触发readyRead
        connect(socket,&QTcpSocket::readyRead,[this,socket]{
            //没有可读的数据就返回
            if(socket->bytesAvailable()<=0)
                return;
            //注意收发两端文本要使用对应的编解码
            const QString recv_text = QString::fromUtf8(socket->readAll());
            QString addr = socket->peerAddress().toString();
            RecvNewTcpMsg(addr, recv_text);
        });
        //连接断开，销毁socket对象，这是为了开关server时socket正确释放
        connect(socket,&QTcpSocket::disconnected,[this,socket]{
            socket->deleteLater();
            QString addr = socket->peerAddress().toString();
            std::map<QString, QTcpSocket*>::iterator iter = clientMap_.find(addr);
            if (iter != clientMap_.end())
                clientMap_.erase(iter);

            std::vector<SqlTableData> seatInfoList = ReadDataFromTable("seatinfo");
            LoadSeatList(seatInfoList);
            emit PostSeatTable(seatInfo_);
        });
    }

    if (socket)
    {
        std::vector<SqlTableData> seatInfoList;
        std::map<QString, SeatInfo>::iterator iter = seatInfo_.find(socket->peerAddress().toString());
        if (iter == seatInfo_.end())
        {
            seatInfoList.push_back({0, {socket->peerAddress().toString()}});
            PushBackDataToTable("seatinfo", {"seat_id"}, seatInfoList);
        }
        seatInfoList = ReadDataFromTable("seatinfo");
        LoadSeatList(seatInfoList);
        emit PostSeatTable(seatInfo_);
    }
}

/**
 * @brief SeatResManager::RecvNewTcpMsg 处理TCP客户端（座位端）发送来的消息（签到/签退）
 * @param addr  TCP客户端的ip地址
 * @param msg   TCP客户端发送的报文
 */
void SeatResManager::RecvNewTcpMsg(QString addr, QString msg)
{
    qDebug() << "recv from " << addr << "   msg:" << msg << endl;
    std::map<QString, SeatInfo>::iterator iter = seatInfo_.find(addr);
    if (iter == seatInfo_.end())
        return;
    SeatInfo seatInfo = iter->second;
    std::map<QString, StuData>::iterator iter_user = stuData_.find(seatInfo.stuid);
    if (iter_user == stuData_.end())
        return;
    StuData stuData = iter_user->second;
    if (msg.indexOf("checked in success") != -1)
    {
        qDebug() << "Checked in success!";
        EditTableItem("usertable", {"stu_id", stuData.stuid}, {"res_state", QString::number(RESSTATE_CHECKED_IN)});
    }
    if (msg.indexOf("checked out success") != -1)
    {
        qDebug() << "Checked out success!";
        seatInfo.startTime.clear();
        seatInfo.stuid = stuData.stuid;
        seatInfo.seatState = SEATSTATE_FREE;
        onPostEditRes(seatInfo);
    }
    std::vector<SqlTableData> seatInfoList = ReadDataFromTable("seatinfo");
    std::vector<SqlTableData> stuDataList = ReadDataFromTable("usertable");
    LoadSeatList(seatInfoList);
    LoadUserList(stuDataList);
    stuData = stuData_[stuData.stuid];
    emit PostSeatTable(seatInfo_);
    emit PostUserState(stuData);
    emit PostUserTable(stuData_);
}

/**
 * @brief SeatResManager::ReadDataFromTable 从seatreservationsystem数据库的指定table读取数据
 * @param tableName
 * @return 读取到的数据
 */
std::vector<SqlTableData> SeatResManager::ReadDataFromTable(QString tableName)
{
    std::vector<SqlTableData> sqlTableData;
    QSqlQuery query;
    query.exec("select * from seatreservationsystem." + tableName);
    while(query.next())
    {
        std::vector<QString> rowData;
        int cnt = query.record().count();
        for (int i = 0; i < cnt; i++) {
            rowData.push_back(query.value(i).toString());
        }
        sqlTableData.push_back({query.value(0).toInt(),rowData});
    }
    return sqlTableData;
}

/**
 * @brief SeatResManager::PushBackDataToTable 向数据库seatreservationsystem的某个table新增数据
 * @param tableName     目标table的名称
 * @param headerList    目标table的头队列
 * @param tableData     需要新增的数据
 * 示例：
 *  std::vector<SqlTableData> reservationList;
 *  reservationList.push_back({0, {"10000", "张三", "1", "2021.12.31 12:00"}});
 *  reservationList.push_back({0, {"10001", "李四", "2", "2022.01.01 17:00"}});
 *  PushBackDataToTable("reservation", {"stu_id", "name", "seat_id", "time"}, reservationList);
 */
void SeatResManager::PushBackDataToTable(QString tableName, QStringList headerList, std::vector<SqlTableData> tableData)
{
    QString header = "(", keyList = "(";
    for (int listIndex = 0; listIndex < headerList.length(); listIndex++) {
        header += headerList.at(listIndex) + ",";
        keyList += ":" + headerList.at(listIndex) + ",";
    }
    header.replace(header.length() - 1, 1, ")");
    keyList.replace(keyList.length() - 1, 1, ")");

    std::vector<QVariantList> tableVariantList;
    tableVariantList.resize(tableData.at(0).rowData.size());
    QSqlQuery query;
    query.prepare("insert into seatreservationsystem." + tableName + header + " values" + keyList);
    for (int tableIndex = 0; tableIndex < tableData.size(); tableIndex++) {
        for (int dataIndex = 0; dataIndex < tableData.at(tableIndex).rowData.size(); dataIndex++) {
            tableVariantList.at(dataIndex) << tableData.at(tableIndex).rowData.at(dataIndex);
        }
    }

    for (int listIndex = 0; listIndex < headerList.length(); listIndex++) {
        query.bindValue(":" + headerList.at(listIndex), tableVariantList.at(listIndex));
    }
    //执行预处理命令
    query.execBatch();
}

/**
 * @brief SeatResManager::DeleteTableRow 删除指定数据库中的某行数据
 * @param tableName
 * @param itemData
 * SQL CMD: DELETE FROM `seatreservationsystem`.`usertable` WHERE (`id` = '1');
 */
void SeatResManager::DeleteTableRow(QString tableName, SqlItemData itemData)
{
    QSqlQuery query;
    QString cmd = "DELETE FROM `seatreservationsystem`.`" + tableName + "` WHERE (`" + itemData.key + "` = '" + itemData.value + "')";
    query.exec(cmd);
    qDebug()<<"cmd"<<cmd;
}

/**
 * @brief SeatResManager::EditTableItem 修改数据库中的某个数据
 * @param tableName
 * @param itemData
 * SQL CMD: UPDATE `seatreservationsystem`.`usertable` SET `stu_id` = '1' WHERE (`id` = '6'); 将usertable表中的id位6的数据的stu_id改为1
 */
void SeatResManager::EditTableItem(QString tableName, SqlItemData targetRow, SqlItemData itemData)
{
    QSqlQuery query;
    QString cmd = "UPDATE `seatreservationsystem`.`" + tableName + "` SET `" + itemData.key + "` = '" + itemData.value + "' WHERE (`" + targetRow.key + "` = '" + targetRow.value + "')";
    query.exec(cmd);
    qDebug()<<"cmd"<<cmd;
}

/**
 * @brief SeatResManager::onPostLoginData 用户登录时触发的槽函数，在该函数中实现用户信息鉴权，实例化相关窗口等操作
 * @param studata   从登录界面回传的用户信息
 */
void SeatResManager::onPostLoginData(StuData studata)
{
    std::map<QString, StuData>::iterator iter = stuData_.find(studata.stuid);
    if (iter == stuData_.end())
        QMessageBox::warning(NULL, "", "无法找到该用户", QMessageBox::Yes);
    else if (iter->second.stuid.compare(studata.stuid) != 0 || iter->second.passwd.compare(studata.passwd) != 0)
        QMessageBox::warning(NULL, "", "学号或密码错误", QMessageBox::Yes);
    else {
        QMessageBox::information(NULL, "", "登陆成功，欢迎你 " + iter->second.name, QMessageBox::Yes);
        dialogLogin_->deleteLater();
        if (iter->second.type.compare("管理员") == 0)
        {
            adminPage_ = new AdminPage(stuData_, seatInfo_);
            adminPage_->show();
            connect(adminPage_, SIGNAL(PostDelUser(QString)), this, SLOT(onPostDelUser(QString)));
            connect(adminPage_, SIGNAL(PostDelSeat(QString)), this, SLOT(onPostDelSeat(QString)));
            connect(adminPage_, SIGNAL(PostEditUserPassword(StuData)), this, SLOT(onPostEditUserPasswd(StuData)));
            connect(adminPage_, SIGNAL(PostDelSeat(QString)), this, SLOT(UnBindSeat(QString)));
            connect(this, SIGNAL(PostUserTable(std::map<QString, StuData>)), adminPage_, SLOT(UpdateUserTable(std::map<QString, StuData>)));
            connect(this, SIGNAL(PostSeatTable(std::map<QString, SeatInfo>)), adminPage_, SLOT(UpdateSeatTable(std::map<QString, SeatInfo>)));
        }
        else
        {
            StuData stuData = iter->second;
            userPage_ = new UserPage(stuData, seatInfo_);
            userPage_->show();
            connect(userPage_, SIGNAL(PostEditUserPassword(StuData)), this, SLOT(onPostEditUserPasswd(StuData)));
            connect(userPage_, SIGNAL(PostEditRes(SeatInfo)), this, SLOT(onPostEditRes(SeatInfo)));
            connect(userPage_, SIGNAL(PostCode2Seat(QString, QString)), this, SLOT(onPostSendCode(QString, QString)));
            connect(this, SIGNAL(PostUserState(StuData)), userPage_, SLOT(UpdateUserState(StuData)));
            connect(this, SIGNAL(PostSeatTable(std::map<QString, SeatInfo>)), userPage_, SLOT(UpdateSeatTable(std::map<QString, SeatInfo>)));
        }
    }
}

/**
 * @brief SeatResManager::onPostRegisterData 用户注册时触发的槽函数，在该函数中实现新用户数据写入等逻辑
 * @param studata   从注册界面回传的用户信息
 */
void SeatResManager::onPostRegisterData(StuData studata)
{
    std::vector<SqlTableData> reservationList;
    std::map<QString, StuData>::iterator iter = stuData_.find(studata.stuid);
    if (iter != stuData_.end())
        QMessageBox::warning(NULL, "", "该用户已存在", QMessageBox::Yes);
    else
    {
        QMessageBox::information(NULL, "", "注册成功", QMessageBox::Yes);
        reservationList.push_back({0, {studata.stuid, studata.name, studata.passwd, studata.type}});
        PushBackDataToTable("usertable", {"stu_id", "name", "passwd", "type"}, reservationList);

        std::vector<SqlTableData> stuDataList = ReadDataFromTable("usertable"); //更新用户列表至缓存
        LoadUserList(stuDataList);
        emit PostUserTable(stuData_);
    }
}

/**
 * @brief SeatResManager::onPostDelUser 管理员界面删除指定用户的槽函数，实现删除数据库中的指定用户数据
 * @param stuid
 */
void SeatResManager::onPostDelUser(QString stuid)
{
    std::map<QString, StuData>::iterator iter = stuData_.find(stuid);
    if (iter == stuData_.end())
        return;
    DeleteTableRow("usertable", {"stu_id", stuid});
    std::vector<SqlTableData> stuDataList = ReadDataFromTable("usertable"); //更新用户列表至缓存
    LoadUserList(stuDataList);
    emit PostUserTable(stuData_);
}

/**
 * @brief SeatResManager::onPostDelUser 管理员界面删除指定座位的槽函数，实现删除数据库中的指定座位数据
 * @param stuid
 */
void SeatResManager::onPostDelSeat(QString seatid)
{
    std::map<QString, SeatInfo>::iterator iter = seatInfo_.find(seatid);
    if (iter == seatInfo_.end())
        return;
    DeleteTableRow("seatinfo", {"seat_id", seatid});
    std::vector<SqlTableData> seatInfoList = ReadDataFromTable("seatinfo");
    LoadSeatList(seatInfoList);
    emit PostSeatTable(seatInfo_);
}

/**
 * @brief SeatResManager::onPostEditUserPasswd 修改用户密码
 * @param studata
 */
void SeatResManager::onPostEditUserPasswd(StuData studata)
{
    std::map<QString, StuData>::iterator iter = stuData_.find(studata.stuid);
    if (iter == stuData_.end())
        return;
    EditTableItem("usertable", {"stu_id", studata.stuid}, {"passwd", studata.passwd});
    std::vector<SqlTableData> stuDataList = ReadDataFromTable("usertable"); //更新用户列表至缓存
    LoadUserList(stuDataList);
    emit PostUserTable(stuData_);
}

/**
 * @brief SeatResManager::onPostEditRes 修改用户预约信息
 * @param seatInfo
 */
void SeatResManager::onPostEditRes(SeatInfo seatInfo)
{
    std::map<QString, StuData>::iterator iter = stuData_.find(seatInfo.stuid);
    if (iter == stuData_.end())
        return;
    StuData studata = iter->second;
    if (seatInfo.seatState == SEATSTATE_BUSY)
    {
        EditTableItem("usertable", {"stu_id", studata.stuid}, {"res_state", QString::number(RESSTATE_NOT_CHECKED_IN)});
        EditTableItem("usertable", {"stu_id", studata.stuid}, {"res_seat_addr", seatInfo.seatAddr});
        EditTableItem("usertable", {"stu_id", studata.stuid}, {"res_time", seatInfo.startTime});
    }
    else if (seatInfo.seatState == SEATSTATE_FREE)
    {
        EditTableItem("usertable", {"stu_id", studata.stuid}, {"res_state", QString::number(RESSTATE_NULL)});
        EditTableItem("usertable", {"stu_id", studata.stuid}, {"res_seat_addr", ""});
        EditTableItem("usertable", {"stu_id", studata.stuid}, {"res_time", ""});
        EditTableItem("usertable", {"stu_id", studata.stuid}, {"limit", ""});
        EditTableItem("usertable", {"stu_id", studata.stuid}, {"limit_time", ""});
        EditTableItem("usertable", {"stu_id", studata.stuid}, {"limit_cnt", ""});
    }

    EditTableItem("seatinfo", {"seat_id", seatInfo.seatAddr}, {"seat_state", QString::number(seatInfo.seatState)});
    if (seatInfo.seatState == SEATSTATE_BUSY)
    {
        EditTableItem("seatinfo", {"seat_id", seatInfo.seatAddr}, {"stu_id", studata.stuid});
        EditTableItem("seatinfo", {"seat_id", seatInfo.seatAddr}, {"start_time", seatInfo.startTime});
    }
    else if (seatInfo.seatState == SEATSTATE_FREE)
    {
        EditTableItem("seatinfo", {"seat_id", seatInfo.seatAddr}, {"stu_id", ""});
        EditTableItem("seatinfo", {"seat_id", seatInfo.seatAddr}, {"start_time", ""});
    }
    std::vector<SqlTableData> stuDataList = ReadDataFromTable("usertable"); //更新用户列表至缓存
    std::vector<SqlTableData> seatInfoList = ReadDataFromTable("seatinfo");
    LoadUserList(stuDataList);
    LoadSeatList(seatInfoList);
    iter = stuData_.find(seatInfo.stuid);
    if (iter == stuData_.end())
        return;
    emit PostUserState(iter->second);
    emit PostSeatTable(seatInfo_);
}

void SeatResManager::onPostSendCode(QString seatAddr, QString code)
{
    std::map<QString, QTcpSocket*>::iterator iter = clientMap_.find(seatAddr);
    QTcpSocket *socket = iter->second;
    socket->write("Passwd:" + code.toUtf8());
}

/**
 * @brief SeatResManager::LoadUserList  解析从数据库获取到的用户组信息，并写入stuData_
 * @param tableData
 */
void SeatResManager::LoadUserList(std::vector<SqlTableData> tableData)
{
    stuData_.clear();
    for (int tableIndex = 0; tableIndex < tableData.size(); tableIndex++) {
        StuData studata;
        SqlTableData tableDataTmp = tableData.at(tableIndex);
        studata.stuid       = tableData.at(tableIndex).rowData.at(1).toUtf8();
        studata.name        = tableData.at(tableIndex).rowData.at(2).toUtf8();
        studata.passwd      = tableData.at(tableIndex).rowData.at(3).toUtf8();
        studata.type        = tableData.at(tableIndex).rowData.at(4).toUtf8();
        studata.resState    = tableData.at(tableIndex).rowData.at(5).toInt();
        studata.resSeatAddr = tableData.at(tableIndex).rowData.at(6).toUtf8();
        studata.resTime     = tableData.at(tableIndex).rowData.at(7).toUtf8();
        studata.limit       = tableData.at(tableIndex).rowData.at(8).toInt();
        studata.limitTime   = tableData.at(tableIndex).rowData.at(9).toUtf8();
        studata.limitCnt    = tableData.at(tableIndex).rowData.at(10).toInt();
        stuData_[studata.stuid] = studata;
    }
}

/**
 * @brief SeatResManager::LoadSeatList  解析从数据库获取到的座位信息，并写入seatInfo__
 * @param tableData
 */
void SeatResManager::LoadSeatList(std::vector<SqlTableData> tableData)
{
    seatInfo_.clear();
    for (int tableIndex = 0; tableIndex < tableData.size(); tableIndex++) {
        SeatInfo seatData;
        SqlTableData tableDataTmp   = tableData.at(tableIndex);
        seatData.seatState          = tableData.at(tableIndex).rowData.at(1).toInt();
        seatData.seatAddr           = tableData.at(tableIndex).rowData.at(2).toUtf8();
        seatData.stuid              = tableData.at(tableIndex).rowData.at(3).toUtf8();
        seatData.startTime          = tableData.at(tableIndex).rowData.at(4).toUtf8();
        seatData.endTime            = tableData.at(tableIndex).rowData.at(5).toUtf8();
        std::map<QString, QTcpSocket*>::iterator iter = clientMap_.find(seatData.seatAddr);
        if (iter != clientMap_.end())
            seatData.isOnline = true;
        else seatData.isOnline = false;
        seatInfo_[seatData.seatAddr] = seatData;
    }
}
